package com.newmuis.open.codeeval.easy;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;

/**
 * CodeEval.com > Easy Challenges > Happy Numbers
 * 
 * Description:
 * 
 * A happy number is defined by the following process. Starting with any positive integer, replace
 * the number by the sum of the squares of its digits, and repeat the process until the number
 * equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those
 * numbers for which this process ends in 1 are happy numbers, while those that do not end in 1 are
 * unhappy numbers.
 * 
 * Input sample:
 * 
 * The first argument is the pathname to a file which contains test data, one test case per line.
 * Each line contains a positive integer. Each line is in the format: N i.e. a positive integer eg.
 * 
 * 1
 * 7
 * 22
 * 
 * Output sample:
 * 
 * If the number is a happy number, print out a 1. If not, print out a 0 eg.
 * 
 * 1
 * 1
 * 0
 * 
 * For the curious, here's why 7 is a happy number: 7->49->97->130->10->1. Here's why 22 is NOT a
 * happy number: 22->8->64->52->29->85->89->145->42->20->4->16->37->58->89 ...
 * 
 * @author Jonathan Newmuis (jon@newmuis.com)
 * @category easy
 * 
 */
public class happy_numbers {
	/**
	 * Calculates the next entry in the sequence after {@code number} by calculating the sum of the
	 * squares of its digits.
	 * 
	 * @param number The number whose succeeding entry should be calculated.
	 * @return The next number in the sequence.
	 */
	private static int calculateNext(final int number) {
		int sum = 0;
		final String str = Integer.toString(number);
		final int length = str.length();

		for (int i = 0; i < length; i++) {
			final int digit = Character.getNumericValue(str.charAt(i));
			sum += digit * digit;
		}

		return sum;
	}

	/**
	 * Determines if a number is happy.
	 * 
	 * @param number The potentially happy number.
	 * @return true if and only if {@code number} is a happy number, as defined in the class
	 *         documentation.
	 */
	private static boolean isHappy(int number) {
		final HashSet<Integer> visited = new HashSet<Integer>();

		while (!visited.contains(number) && number != 1) {
			visited.add(number);
			number = calculateNext(number);
		}

		return number == 1;
	}

	/**
	 * @param args should contain only one CLI argument: the path to an input file which specifies
	 *            the input in the defined format.
	 * @throws IOException if there is a problem reading the given input file.
	 */
	public static void main(final String[] args) throws IOException {
		// Verify input argument length.
		if (args.length < 1) {
			throw new RuntimeException("Must specify input file path.");
		} else if (args.length > 1) {
			throw new RuntimeException("Illegal input arguments specified.");
		}

		// Read file from specified path.
		final BufferedReader reader = new BufferedReader(new FileReader(new File(args[0])));
		String line;

		while ((line = reader.readLine()) != null) {
			try {
				final int number = Integer.parseInt(line);
				System.out.println(isHappy(number) ? 1 : 0);
			} catch (final NumberFormatException e) {
				throw new RuntimeException("Input file contained non-numeric arguments.");
			}
		}
	}
}
